package org.jeecg.zhenxuan.CommonUtils;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.collections.MapUtils;
import org.springframework.util.StringUtils;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;

//import sun.misc.BASE64Encoder;

public class CommonUtils {
    public static int randomSixDigital() {
        Random random = new Random();
        int num = random.nextInt(999999);
        num = num < 100000 ? num + 100000 : num;
        return num;
    }


    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CommonUtils.class);

    /**
     * add begin
     * 2019年11月19日 星期二
     */
    /**
     * 生成JWT
     *
     * @throws UnsupportedEncodingException
     */
    public static String generateJWT(String secret, SignatureAlgorithm signatureAlgorithm, Long expirationMillisecond, Map headers, Map claims) {

        Long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        byte[] encodedKey = new byte[0];
        try {
            encodedKey = secret.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("生成JWT出错！");
        }

        SecretKey secretKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");


        /**
         * 生成JWT时，携带信息 claims 和 payload 二选一
         * 一种是map
         * 一种可以选择用JSON，或者序列化之后的对象
         * 使用claims之后会把 setId、setIssuer、setSubject .. 覆盖
         * 使用payload时，自己需要携带的任何信息都要用payload
         *
         * 推荐使用claims
         */

        JwtBuilder jwtBuilder = Jwts.builder()
                .signWith(signatureAlgorithm, secretKey);

        if (MapUtils.isNotEmpty(headers)) {
            jwtBuilder.setHeader(headers);
        }
        if (MapUtils.isNotEmpty(claims)) {
            jwtBuilder.setClaims(claims);
        }


        Date expiration = new Date(nowMillis + expirationMillisecond);

        jwtBuilder.setExpiration(expiration);

        log.info(jwtBuilder.compact());
        log.info(jwtBuilder.toString());

        return jwtBuilder.compact();
    }

    /**
     * 解析JWT
     *
     * @throws UnsupportedEncodingException
     */
    public static Claims translateJWT(String jwt, String secret, SignatureAlgorithm signatureAlgorithm, Long expirationMillisecond) {

        Long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        byte[] encodedKey = new byte[0];
        try {
            encodedKey = secret.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            log.error("解析JWT时，encode密钥时出错！");
        }

        SecretKey secretKey = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");


        try {
            String signature = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getSignature();

            io.jsonwebtoken.Header header = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getHeader();

            Claims claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();


            log.info("############## signature ##########################################################");
            log.info(signature);

            log.info("################# header #################################################");

    /*        for (int i = 0; i < header.keySet().size(); i++) {
                header.values().
            }*/

/*
            header.keySet().forEach(k -> {
                log.info(k + "：" + header.get(k).toString());
            });
*/

            log.info("############ claims ############################################################");

 /*           claims.keySet().forEach(k -> {
                log.info(k + "：" + claims.get(k).toString());
            });*/

            log.info("########################## expiration #######################################");
            log.info(claims.getExpiration().toLocaleString());
            log.info("currentTimeMillis : " + System.currentTimeMillis());
            log.info("JWT  expiration TimeMillis : " + claims.getExpiration().getTime());
            if (System.currentTimeMillis() > claims.getExpiration().getTime()) {
                log.warn("JWT expirationTime had been over !");
            } else {
                return claims;
            }

        } catch (io.jsonwebtoken.ExpiredJwtException eje) {
            eje.getStackTrace();
            log.error(eje.getLocalizedMessage());
            log.info("############## signature ##########################################################");
//            log.info(signature);

            log.info("################# header #################################################");
/*
            eje.getHeader().keySet().forEach(k -> {
                log.info(k + "：" + eje.getHeader().get(k).toString());
            });
*/

            log.info("############ claims ############################################################");

/*            eje.getClaims().keySet().forEach(k -> {
                log.info(k + "：" + eje.getClaims().get(k).toString());
            });*/
        } catch (io.jsonwebtoken.MalformedJwtException mje) {
            mje.getStackTrace();
            log.error(mje.getLocalizedMessage());
        }

        return null;

    }


    /**
     * 将网络图片编码为base64
     *
     * @param url
     * @return
     * @throws Exception
     */
/*
    public static String encodeImageToBase64(URL url) throws Exception {
        //将图片文件转化为字节数组字符串，并对其进行Base64编码处理
        System.out.println("图片的路径为:" + url.toString());
        //打开链接
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) url.openConnection();
            //设置请求方式为"GET"
            conn.setRequestMethod("GET");
            //超时响应时间为5秒
            conn.setConnectTimeout(5 * 1000);
            //通过输入流获取图片数据
            InputStream inStream = conn.getInputStream();
            //得到图片的二进制数据，以二进制封装得到数据，具有通用性
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            //创建一个Buffer字符串
            byte[] buffer = new byte[1024];
            //每次读取的字符串长度，如果为-1，代表全部读取完毕
            int len = 0;
            //使用一个输入流从buffer里把数据读取出来
            while ((len = inStream.read(buffer)) != -1) {
                //用输出流往buffer里写入数据，中间参数代表从哪个位置开始读，len代表读取的长度
                outStream.write(buffer, 0, len);
            }
            //关闭输入流
            inStream.close();
            byte[] data = outStream.toByteArray();
            //对字节数组Base64编码
            BASE64Encoder encoder = new BASE64Encoder();
            String base64 = encoder.encode(data);
            return base64;//返回Base64编码过的字节数组字符串
        } catch (IOException e) {
            e.printStackTrace();
            throw new Exception("图片转base64失败");
        }
    }
*/


//    @Test
    public void createQrCode(){
        String qrUrl = "https://www.baidu.com";
        String note = "";
        File logoFile = new File("D://prlogoImg.png");//logo图片路径
        File codeFile = new File("D://prlogoImg222.png");//生成后图片的输出地址
//        drawLogoQRCode(logoFile, codeFile, qrUrl, note);
    }


    /**
     * 生成带Logo的二维码Base64
     * @param logoFile
     * @param qrUrl
     * @param note
     * @return
     */
    public static String drawLogoQRCode(File logoFile, String qrUrl, String note) {//图片文件  二维码储存地址 网页路径          二维码说明
        try {
            MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
            // 参数顺序分别为：编码内容，编码类型，生成图片宽度，生成图片高度，设置参数
            BitMatrix bm = multiFormatWriter.encode(qrUrl, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints);
            BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

            // 开始利用二维码数据创建Bitmap图片，分别设为黑（0xFFFFFFFF）白（0xFF000000）两色
            for (int x = 0; x < WIDTH; x++) {
                for (int y = 0; y < HEIGHT; y++) {
                    image.setRGB(x, y, bm.get(x, y) ? QRCOLOR : BGWHITE);
                }
            }
            int width = image.getWidth();
            int height = image.getHeight();
            if (Objects.nonNull(logoFile) && logoFile.exists()) {
                // 构建绘图对象
                Graphics2D g = image.createGraphics();
                // 读取Logo图片
                BufferedImage logo = ImageIO.read(logoFile);
                // 开始绘制logo图片
                g.drawImage(logo, width * 2 / 5, height * 2 / 5, width * 2 / 10, height * 2 / 10, null);
                g.dispose();
                logo.flush();
            }

            // 自定义文本描述
            if (StringUtils.hasLength(note)) {
                // 新的图片，把带logo的二维码下面加上文字
                BufferedImage outImage = new BufferedImage(400, 445, BufferedImage.TYPE_4BYTE_ABGR);
                Graphics2D outg = outImage.createGraphics();
                // 画二维码到新的面板
                outg.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
                // 画文字到新的面板
                outg.setColor(Color.BLACK);
                outg.setFont(new Font("楷体", Font.BOLD, 30)); // 字体、字型、字号
                int strWidth = outg.getFontMetrics().stringWidth(note);
                if (strWidth > 399) {
                    // //长度过长就截取前面部分
                    // 长度过长就换行
                    String note1 = note.substring(0, note.length() / 2);
                    String note2 = note.substring(note.length() / 2, note.length());
                    int strWidth1 = outg.getFontMetrics().stringWidth(note1);
                    int strWidth2 = outg.getFontMetrics().stringWidth(note2);
                    outg.drawString(note1, 200 - strWidth1 / 2, height + (outImage.getHeight() - height) / 2 + 12);
                    BufferedImage outImage2 = new BufferedImage(400, 485, BufferedImage.TYPE_4BYTE_ABGR);
                    Graphics2D outg2 = outImage2.createGraphics();
                    outg2.drawImage(outImage, 0, 0, outImage.getWidth(), outImage.getHeight(), null);
                    outg2.setColor(Color.BLACK);
                    outg2.setFont(new Font("宋体", Font.BOLD, 30)); // 字体、字型、字号
                    outg2.drawString(note2, 200 - strWidth2 / 2,outImage.getHeight() + (outImage2.getHeight() - outImage.getHeight()) / 2 + 5);
                    outg2.dispose();
                    outImage2.flush();
                    outImage = outImage2;
                } else {
                    outg.drawString(note, 200 - strWidth / 2, height + (outImage.getHeight() - height) / 2 + 12); // 画文字
                }
                outg.dispose();
                outImage.flush();
                image = outImage;
            }

            image.flush();

            ByteArrayOutputStream os = new ByteArrayOutputStream();



//      ImageIO.write(image, "png", codeFile); // TODO

//        BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bm);
            ImageIO.write(image, "png", os);


            /**
             * 原生转码前面没有 data:image/png;base64 这些字段，返回给前端是无法被解析，可以让前端加，也可以在下面加上
             */

            String base64str = org.springframework.util.Base64Utils.encodeToString(os.toByteArray());

            String resultImage = new String( base64str);

//        return resultImage;

//            log.debug(resultImage);

            return resultImage;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static final int QRCOLOR = 0xFF000000; // 默认是黑色
    private static final int BGWHITE = 0xFFFFFFFF; // 背景颜色

    private static final int WIDTH = 500; // 二维码宽
    private static final int HEIGHT = 500; // 二维码高

    // 用于设置QR二维码参数
    private static
    Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>() {
        private static final long serialVersionUID = 1L;
        {
            put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);// 设置QR二维码的纠错级别（H为最高级别）具体级别信息
            put(EncodeHintType.CHARACTER_SET, "utf-8");// 设置编码方式
            put(EncodeHintType.MARGIN, 0);
        }
    };

//    @Test
    public void createQrCodeTest(){
        String qrUrl = "https://www.baidu.com";
        String note = "";
        File logoFile = new File("D://prlogoImg.png");//logo图片路径
        File codeFile = new File("D://prlogoImg222.png");//生成后图片的输出地址
//        drawLogoQRCode(logoFile, codeFile, qrUrl, note);
    }


    /**
     * 生成二维码的Base64字符串
     * @param content
     * @param width
     * @param height
     * @return
     * @throws IOException
     */
    public static String crateQRCodeForBase64(String content, int width, int height) throws IOException {

        String resultImage = "";
        if (!StringUtils.isEmpty(content)) {
            ServletOutputStream stream = null;
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            @SuppressWarnings("rawtypes")
            HashMap<EncodeHintType, Comparable> hints = new HashMap<>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); // 指定字符编码为“utf-8”
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); // 指定二维码的纠错等级为中级
            hints.put(EncodeHintType.MARGIN, 2); // 设置图片的边距

            try {
                QRCodeWriter writer = new QRCodeWriter();
                BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);

                BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
                ImageIO.write(bufferedImage, "png", os);
                /**
                 * 原生转码前面没有 data:image/png;base64 这些字段，返回给前端是无法被解析，可以让前端加，也可以在下面加上
                 */

                String base64str = org.springframework.util.Base64Utils.encodeToString(os.toByteArray());

//                resultImage = new String("data:image/png;base64," + base64str);
                resultImage = new String( base64str);

                return resultImage;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (stream != null) {
                    stream.flush();
                    stream.close();
                }
            }
        }
        return null;
    }


}
